package no.nordicsemi.android.nrfmesh.auth.ui;

import android.os.Looper;

import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import no.nordicsemi.android.nrfmesh.auth.data.AccountRepository;
import no.nordicsemi.android.nrfmesh.database.entity.LocalAccount;

/**
 * AuthViewModel
 *
 * 用途：
 * - 登录页/注册流程共享临时数据
 * - 提供“本机已保存账号列表”“预填最近账号”等状态
 *
 * 线程模型：
 * - 所有 DB I/O 放入单线程 io 池
 * - LiveData 更新用 safeSet*（主线程 setValue / 子线程 postValue）
 */
public class AuthViewModel extends ViewModel {

    /* ========== 注册向导临时数据（按你原样保留，需要的话用） ========== */
    public String role; // "USER" or "DOCTOR"
    public String id;
    public char[] pd;

    // 用户资料
    public String uName, uGender;
    public Integer uAge;

    // 医生资料
    public String dName, dHospital;

    // 注册页偏好
    public Boolean rememberOnDevice;
    public Boolean autoLoginAfterReg;

    /* ========== 账户仓库（Room + 加密偏好） ========== */
    private AccountRepository accountRepo;

    /* ========== LiveData ========== */
    private final MutableLiveData<List<LocalAccount>> _accounts =
            new MutableLiveData<>(new ArrayList<>());
    public final LiveData<List<LocalAccount>> accounts = _accounts;

    private final MutableLiveData<String> _prefilledUser =
            new MutableLiveData<>(null);
    public final LiveData<String> prefilledUser = _prefilledUser;

    /* ========== 单线程 I/O 池 ========== */
    private final ExecutorService io = Executors.newSingleThreadExecutor();

    /** 由宿主在 onCreate 时注入仓库，随后加载列表并预填最近账号 */
    public void attachAccountRepo(AccountRepository repo) {
        this.accountRepo = repo;
        loadAccounts();         // 异步加载
        prefillFromLastUser();  // 轻量读取
    }

    /* ---------------- 工具：更新 LiveData ---------------- */
    private boolean isMainThread() {
        return Looper.myLooper() == Looper.getMainLooper();
    }

    private void safeSetAccounts(List<LocalAccount> list) {
        if (isMainThread()) _accounts.setValue(list);
        else _accounts.postValue(list);
    }

    private void safeSetPrefilled(String userId) {
        if (isMainThread()) _prefilledUser.setValue(userId);
        else _prefilledUser.postValue(userId);
    }

    /* ---------------- 列表/预填 ---------------- */

    /**
     * 异步加载本机账号列表：
     * - 优先调用 AccountRepository.listAsync(callback)
     * - 如果项目里没有该方法，则回退到 io 线程里的 list()
     */
    public void loadAccounts() {
        if (accountRepo == null) return;

        try {
            // 反射查找 listAsync(java.util.function.Consumer)
            Method m = accountRepo.getClass().getMethod("listAsync", java.util.function.Consumer.class);
            // 调用 listAsync，并在回调里更新 LiveData
            m.invoke(accountRepo, (java.util.function.Consumer<List<LocalAccount>>) this::safeSetAccounts);
        } catch (NoSuchMethodException nsme) {
            // 回退方案：自己切到 io 线程调用同步 list()
            io.execute(() -> {
                List<LocalAccount> list = accountRepo.list(); // 确保仓库不会再抛“主线程访问”的自检异常
                safeSetAccounts(list);
            });
        } catch (Throwable t) {
            // 极端情况直接回退一次，避免整个页面空白
            io.execute(() -> {
                List<LocalAccount> list = accountRepo.list();
                safeSetAccounts(list);
            });
        }
    }

    /** 预填最近一次登录账号（来自加密偏好；轻量操作） */
    private void prefillFromLastUser() {
        if (accountRepo == null) return;
        String last = accountRepo.getLastUser();
        if (last != null) safeSetPrefilled(last);
    }

    /* ---------------- 自动登录判定 ---------------- */

    /**
     * 尝试自动登录：满足 last_user + autoLogin + 有本地密文密码 时，返回 userId；否则 null
     */
    public @Nullable String tryAutoLoginUserId() {
        if (accountRepo == null) return null;
        String last = accountRepo.getLastUser();
        if (last == null) return null;
        boolean can = accountRepo.isAutoLogin(last)
                && accountRepo.getSavedPassword(last) != null;
        return can ? last : null;
    }

    /** 提供密码给外部（自动登录用） */
    public @Nullable String getSavedPassword(String userId) {
        if (accountRepo == null) return null;
        return accountRepo.getSavedPassword(userId);
    }

    /* ---------------- 新增：统一的“认证成功后”入口（无密码场景） ---------------- */

    /**
     * 注册流程完成后（或第三方登录完成后）调用：
     * - 仅保证本机 LocalAccount 存在、刷新最近使用、自动登录标志；不保存明文密码。
     * - 与 onLoginSucceeded 的区别：不需要 plainPwd。
     */
    public void onAuthSuccess(String userId, @Nullable String displayName,
                              boolean remember, boolean autoLogin) {
        if (accountRepo == null) return;
        io.execute(() -> {
            // 1) 确保本机索引存在（即使未勾“记住”，也入表但不存密码）
            ensureLocalIndex(userId, displayName, autoLogin);
            // 2) 刷新最近/最近用户/自动登录
            accountRepo.touch(userId);
            accountRepo.setLastUser(userId);
            accountRepo.setAutoLogin(userId, autoLogin);
            // 3) 刷新列表与预填
            loadAccounts();
            prefillFromLastUser();
        });
    }

    /* ---------------- 登录成功持久化（原方法：增强） ---------------- */

    /**
     * 登录成功后调用：
     * 1) 保证 LocalAccount 索引存在（新增 ensureLocalIndex；即使未勾“记住”也会入表，但不保存密码）
     * 2) touch 最近时间 + 设置 last_user + 设置 autoLogin
     * 3) 记住账号（仅在 remember==true 时保存密文密码）
     * 4) 刷新列表
     *
     * 注意：全部在 I/O 线程执行，避免主线程 Room 访问
     */
    public void onLoginSucceeded(String userId, String displayName,
                                 boolean remember, boolean autoLogin, String plainPwd) {
        if (accountRepo == null) return;
        io.execute(() -> {
            // (A) 新增：确保本机索引存在（不依赖“记住此账号到本机”开关）
            ensureLocalIndex(userId, displayName, autoLogin);

            // (B) 你原有逻辑：最近时间 & 最近用户
            accountRepo.touch(userId);
            accountRepo.setLastUser(userId);

            // (C) 仅在勾选“记住到本机”时，才保存密文密码
            if (remember) {
                accountRepo.remember(userId, displayName, plainPwd, autoLogin);
            } else {
                // 未勾选也同步 autoLogin 标志（通常为 false；保持与 UI 同步）
                accountRepo.setAutoLogin(userId, autoLogin);
            }

            // (D) 刷新视图层
            loadAccounts(); // 异步刷新列表
            prefillFromLastUser();
        });
    }

    /* ---------------- 账号管理 ---------------- */

    /** 删除已保存账号（仓库内部会处理 last_user 回退） */
    public void deleteAccount(String userId) {
        if (accountRepo == null) return;
        io.execute(() -> {
            accountRepo.deleteLocalAccount(userId);
            loadAccounts();
            prefillFromLastUser();
        });
    }

    /** 切换自动登录开关 */
    public void setAccountAutoLogin(String userId, boolean enabled) {
        if (accountRepo == null) return;
        io.execute(() -> {
            accountRepo.setAutoLogin(userId, enabled);
            loadAccounts();
        });
    }

    @Override protected void onCleared() {
        super.onCleared();
        io.shutdown();
    }

    /* ====================== 私有工具：保证存在本机索引 ====================== */


    private void ensureLocalIndex(String userId, @Nullable String displayName, boolean autoLogin) {
        try {
            // 方式 1：upsertLocalAccount(LocalAccount)
            Method upsert = accountRepo.getClass().getMethod("upsertLocalAccount", LocalAccount.class);
            upsert.invoke(accountRepo, new LocalAccount(userId, displayName, /*rememberOnDevice*/ false,
                    autoLogin, System.currentTimeMillis()));
            return;
        } catch (Throwable ignore) { /* no-op */ }

        try {
            // 方式 2：saveLocalAccount(String userId, String displayName, boolean autoLogin)
            Method save = accountRepo.getClass().getMethod("saveLocalAccount", String.class, String.class, boolean.class);
            save.invoke(accountRepo, userId, displayName, autoLogin);
            return;
        } catch (Throwable ignore) { /* no-op */ }

        try {
            // 方式 3：退化到 remember(..., null, autoLogin) —— 不保存密码，仅让仓库写索引
            Method remember = accountRepo.getClass()
                    .getMethod("remember", String.class, String.class, String.class, boolean.class);
            remember.invoke(accountRepo, userId, displayName, /*plainPwd*/ null, autoLogin);
        } catch (Throwable ignore) {
            // 如果仓库既不支持 upsert/save，也不能接受 null 密码，则索引插入将被跳过。
            // 这种情况下，登录页的“最近使用”仍可依赖 setLastUser() 显示预填账号。
        }
    }
}
